/*
 * Copyright (C) Hisilicon Technologies Co., Ltd. 2018-2019. All rights reserved.
 * Description: upgrade module upgrade_crc.c
 * Author: HiMobileCam Reference Develop Team
 * Create: 2018-07-05
 */

#include "crc.h"
#include <hi_type.h>

#define UPGRADE_CPU_TO_LE32(x)        (x)
#define UPGRADE_LE32_TO_CPU(x)        (x)
#define UPGRADE_TABLE_SIZE            256U

static HI_S32 g_CRC_tableEmpty = 1;
static HI_U32 g_CRC_table[UPGRADE_TABLE_SIZE];

static HI_VOID UPGRADE_MakeCRCTable(HI_VOID)
{
    /* terms of polynomial defining this crc (except x^32): */
    static const HI_U8 polynomialTab[] = {0U, 1U, 2U, 4U, 5U, 7U, 8U, 10U, 11U, 12U, 16U, 22U, 23U, 26U};
    HI_U32 i, j, c;
    /* make exclusive-or pattern from polynomial (0xedb88320L) */
    HI_UL poly = 0L;
    for (i = 0; i < (sizeof(polynomialTab) / sizeof(HI_U8)); i++) {
        poly |= 1L << (31U - polynomialTab[i]);
    }

    for (i = 0; i < UPGRADE_TABLE_SIZE; i++) {
        c = (HI_UL)i;
        for (j = 0; j < 8U; j++) {
            c = (c & 1) ? (poly ^ (c >> 1)) : (c >> 1);
        }
        g_CRC_table[i] = UPGRADE_CPU_TO_LE32(c);
    }
    g_CRC_tableEmpty = 0;
}

#if __BYTE_ORDER == __LITTLE_ENDIAN
#define DO_CRC(x) (crc = tab[(crc ^ (x)) & 0xFFU] ^ (crc >> 8U))
#else
#define DO_CRC(x) (crc = tab[((crc >> 24U) ^ (x)) & 0xFFU] ^ (crc << 8U)) /* >>24: to get the lower byte */
#endif

static HI_U32 UPGRADE_NoComp(HI_U32 crc, HI_U8 const * buf, HI_U32 len)
{
    const HI_U32* tab = g_CRC_table;
    const HI_U32* srcPtr = (const HI_U32 *)buf;
    HI_U8 *tmpPtr = NULL;

    if (g_CRC_tableEmpty) {
        UPGRADE_MakeCRCTable();
    }

    crc = UPGRADE_CPU_TO_LE32(crc);
    if ((((uintptr_t)srcPtr) & 3U) && len) { /* Align it */
        tmpPtr = (HI_U8 *)srcPtr;
        do {
            DO_CRC(*tmpPtr++);
        }
        while ((--len) && (((uintptr_t)tmpPtr) & 3U));
        srcPtr = (HI_U32 *)tmpPtr;
    }

    size_t rem_len = len & 3U;
    len = len >> 2U;
    for (--srcPtr; len; --len) {
        /* load data 32 bits wide, xor data 32 bits wide. */
        crc ^= *++srcPtr; /* use pre increment for speed */
        DO_CRC(0);
        DO_CRC(0);
        DO_CRC(0);
        DO_CRC(0);
    }
    len = rem_len;
    /* And the last few bytes */
    if (len) {
        tmpPtr = (HI_U8 *)(srcPtr + 1) - 1;
        do {
            DO_CRC(*++tmpPtr); /* use pre increment for speed */
        }
        while (--len);
    }

    return UPGRADE_LE32_TO_CPU(crc);
}
#undef DO_CRC

uint32_t __crc32(uint32_t crc,  unsigned char* p, uint32_t len)
{
    return UPGRADE_NoComp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL;
}

